package cn.sangyy.user.service.impl;

import cn.sangyy.basic.common.code.CodeEnum;
import cn.sangyy.basic.constent.BaseConstants;
import cn.sangyy.basic.constent.WxConstants;
import cn.sangyy.basic.exception.BusinessException;
import cn.sangyy.basic.exception.CarAssert;
import cn.sangyy.basic.utils.HttpUtil;
import cn.sangyy.basic.utils.MD5Utils;
import cn.sangyy.user.domain.LoginInfo;
import cn.sangyy.user.domain.User;
import cn.sangyy.user.domain.UserWx;
import cn.sangyy.user.dto.BindWechatDto;
import cn.sangyy.user.dto.RegisterInfoDto;
import cn.sangyy.basic.dto.SendEmailDto;
import cn.sangyy.basic.dto.SmsDto;
import cn.sangyy.user.mapper.LoginInfoMapper;
import cn.sangyy.user.mapper.UserMapper;
import cn.sangyy.basic.raabbitmq.config.RegisterNoticeRabbitmqConfig;
import cn.sangyy.user.service.ILoginService;
import cn.sangyy.user.service.IUserService;
import cn.sangyy.user.service.IUserWxService;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import com.github.mustachejava.Code;
import org.apache.http.client.utils.HttpClientUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author sangyangyang
 * @since 2023-06-02
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired
    private LoginInfoMapper loginInfoMapper;

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private IUserWxService userWxService;

    @Autowired
    private ILoginService loginService;

    /**
    * @author Sangyy
    * 手机短信注册
    */
    @Override
    @Transactional
    public void phoneRegister(RegisterInfoDto dto) {
        //参数校验
        //验证码校验
        paramVerify(dto);
        //校验通过
        //保存登录信息
        LoginInfo loginInfo = new LoginInfo();
        saveLoginInfo(dto,loginInfo);
        //保存用户信息
        User user = new User();
        BeanUtils.copyProperties(loginInfo,user);
        user.setId(null);
        user.setAge(dto.getAge());
        user.setCreateTime(new Date());
        user.setLoginInfoId(loginInfo.getId());
        user.setSex(dto.getSex());
        user.setState(1);
        insert(user);
        //mq发送注册成功消息（邮箱消息，短信消息）
        SendEmailDto emailDto = new SendEmailDto(dto.getEmail(), "用户注册成功", 
                "收到邮件通知：你好！你在蓉车宝网站注册的账号已经完成，可以正常使用了！");
        rabbitTemplate.convertAndSend(RegisterNoticeRabbitmqConfig.EXCHANGE_TOPICS_INFORM,
                "inform.email",emailDto);
        SmsDto smsDto = new SmsDto(dto.getMobile(), "你好！你在蓉车宝网站注册的账号已经完成可以正常使用了。");
        rabbitTemplate.convertAndSend(RegisterNoticeRabbitmqConfig.EXCHANGE_TOPICS_INFORM,
                "inform.sms",smsDto);
    }

    @Transactional
    @Override
    public Map<String, Object> bindWechat(BindWechatDto dto) {
        //手机验证码校验
        String phone = dto.getPhone();
        String verifyCode = dto.getVerifyCode();
        String openid = dto.getOpenid();
        String accessToken = dto.getAccessToken();
        String phoneKeyInRedis = BaseConstants.BIND_PHONE_CODE_IN_REDIS+phone;
        String codeInRedis = (String) redisTemplate.opsForValue().get(phoneKeyInRedis);
        CarAssert.assertStrNotBlank(codeInRedis,CodeEnum.REGISTER_PHONE_CODE_EXPIRED);
        CarAssert.assertStrEquals(verifyCode,codeInRedis, CodeEnum.CODE_ERROR);
        //通过acccessToken和openid拿到微信用户信息
        String getUserUrl = WxConstants.GET_USER_URL
                .replace("ACCESS_TOKEN", accessToken)
                .replace("OPENID", openid);
        String wxUserStr = HttpUtil.httpGet(getUserUrl);
        UserWx userWx = getUerWx(wxUserStr);
        //判断手机号是否已存在
        List<User> users = userMapper.selectList(new EntityWrapper<User>().eq("phone", phone));
        User user;
        LoginInfo loginInfo;
        if(users != null && users.size() > 0){
            user = users.get(0);
            //判断该手机号是否绑定了微信
            UserWx userWx1 = userWxService.selectOne(new EntityWrapper<UserWx>().eq("user_id", user.getId()));
            if(Objects.nonNull(userWx1)){//绑定了直接抛异常
                throw new BusinessException(CodeEnum.BIND_PHONE_WECHAT_IS_EXIST);
            }else {
                //未绑定，直接绑定，并返回登录信息
                loginInfo = loginInfoMapper.selectList(new EntityWrapper<LoginInfo>().
                        eq("id", user.getLoginInfoId())).get(0);
            }
        }else { //添加user、userLoginInfo、userWx
            loginInfo = addLoginInfo(phone);
            loginInfoMapper.insert(loginInfo);
            user = addUser(loginInfo);
            userMapper.insert(user);
        }
        userWx.setUserId(user.getId());
        userWxService.insert(userWx);
        return loginService.backLoginInfo(loginInfo);
    }

    private User addUser(LoginInfo loginInfo) {
        User user = new User();
        BeanUtils.copyProperties(loginInfo,user);
        user.setId(null);
        user.setState(1);
        user.setLoginInfoId(loginInfo.getId());
        user.setCreateTime(new Date());
        return user;
    }

    private LoginInfo addLoginInfo(String phone) {
        LoginInfo loginInfo = new LoginInfo();
        loginInfo.setType(1);
        loginInfo.setDisable(1);
        loginInfo.setUsername("rcb"+UUID.randomUUID());
        loginInfo.setPhone(phone);
        String salt = UUID.randomUUID().toString();
        loginInfo.setSalt(salt);
        //发送短信告诉默认密码 123456
        loginInfo.setPassword("car"+salt+"register123456");
        return loginInfo;
    }

    private UserWx getUerWx(String wxUserStr) {
        JSONObject wxUserObj = JSONObject.parseObject(wxUserStr);
        UserWx result = new UserWx();
        result.setOpenid(wxUserObj.getString("openid"));
        result.setNickname(wxUserObj.getString("nickname"));
        result.setSex(wxUserObj.getInteger("sex"));
        result.setCountry(wxUserObj.getString("country"));
        result.setProvince(wxUserObj.getString("province"));
        result.setHeadimgurl(wxUserObj.getString("headimgurl"));
        result.setUnionid(wxUserObj.getString("unionid"));
        return result;
    }

    private void paramVerify(RegisterInfoDto dto) {
        String mobile = dto.getMobile();
        String codeKeyInRedis = BaseConstants.REGISTER_PHONE_CODE_IN_REDIS+mobile;
        String code = (String)redisTemplate.opsForValue().get(codeKeyInRedis);
        CarAssert.assertStrNotBlank(code,CodeEnum.REGISTER_PHONE_CODE_EXPIRED);
        //两次密码比对校验
        CarAssert.assertStrEquals(dto.getPassword(), dto.getConfirmPassword(),CodeEnum.TWO_PASSWORD_ERROR);
        //判断用户是否注册过
        int count = selectCount(new EntityWrapper<User>()
                .eq("phone", dto.getMobile()));
        //手机号已被注册
        if(count > 0){
            throw new BusinessException(CodeEnum.REGISTER_PHONE_IS_EXIST);
        }
        count = selectCount(new EntityWrapper<User>().eq("email", dto.getEmail()));
        if(count > 0){
            throw new BusinessException(CodeEnum.REGISTER_EMAIL_IS_EXIST);
        }
        count = selectCount(new EntityWrapper<User>().eq("username", dto.getUsername()));
        if(count > 0){
            throw new BusinessException(CodeEnum.REGISTER_USERNAME_IS_EXIST);
        }
    }

    private void saveLoginInfo(RegisterInfoDto dto,LoginInfo loginInfo) {
        loginInfo.setPhone(dto.getMobile());
        loginInfo.setEmail(dto.getEmail());
        loginInfo.setDisable(1); //1正常0禁止
        loginInfo.setType(1); //1用户，0员工
        loginInfo.setUsername(dto.getUsername());
        String salt = UUID.randomUUID().toString();
        String password = MD5Utils.encrypByMd5("car" + salt + "register" + dto.getPassword());
        loginInfo.setSalt(salt);
        loginInfo.setPassword(password);
        loginInfoMapper.insert(loginInfo);
    }
}
